/*
 * COPYRIGHT:	2010 Christopher Smith, licenced under the GNU GPL v2; see COPYING in the top level directory
 * PROJECT:		sdedit
 * FILE:		ntds ldap enum guid.cpp
 * PURPOSE:		NTDS over LDAP support
 *				type GUID enumeration functions
 */

namespace Ldap
{
using namespace Lookup;


class ObjInhTypeGuidEnumerator : public Enumerator<GUID>
{
	auto_refct<LdapConnection> Lc;

public:
	ObjInhTypeGuidEnumerator(LdapConnection *Lc) :
		Lc(Lc)
	{}

	virtual void WalkChildren(ChildrenWalkDest<GUID> &Dest) override
	{} //no children

	virtual void WalkEntries(EntriesWalkDest<GUID> &Dest) override
	{
		PWSTR Attrs[] = {L"cn", L"schemaIDGUID", NULL};

		ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Schema,CN=Configuration", SbtForest, L"(&(objectClass=classSchema)(!(objectClassCategory=2))(!(objectClassCategory=3)))");

		auto_free<LDAPMessage, &free_ldap_message> SearchResult;

		AwaitSearchResult(Lc, OpNum, SearchResult);

		LDAPMessage *Entry = ldap_first_entry(Lc->ld, SearchResult);

		while(Entry)
		{
			auto_free<PWSTR, free_ldap_value> CnValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"cn"));
			auto_free<berval *, free_ldap_value_len> GuidValues = auto_take_(ldap_get_values_len(Lc->ld, Entry, L"schemaIDGUID"));

			GUID &Guid = *c_cast<GUID *>(GuidValues[0]->bv_val);

			Dest.EntryProc(CnValues[0], Guid);

			Entry = ldap_next_entry(Lc->ld, Entry);
		}
	}
};

class ObjTypeGuidEnumerator : public Enumerator<GUID>
{
	auto_refct<LdapConnection> Lc;

public:
	ObjTypeGuidEnumerator(LdapConnection *Lc) :
		Lc(Lc)
	{}

private:
	struct ClassEnumForAttributes : public Enumerator<GUID>
	{
		struct ClassAttributeEnum : public Enumerator<GUID>
		{
			auto_refct<LdapConnection> Lc;
			auto_szpod<WCHAR> ClassCn;

			ClassAttributeEnum(LdapConnection *Lc, PCWSTR ClassCn) :
				Lc(Lc),
				ClassCn(ClassCn)
			{}

			virtual void WalkChildren(ChildrenWalkDest<GUID> &Dest) override
			{
				PWSTR Attrs[] = {L"subClassOf", L"systemAuxiliaryClass", L"auxiliaryClass", NULL};
				
				ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Schema,CN=Configuration", SbtForest, L"(&(cn=%s)(objectClass=classSchema))", ClassCn);

				auto_free<LDAPMessage, &free_ldap_message> SearchResult;

				AwaitSearchResult(Lc, OpNum, SearchResult);

				LDAPMessage *Entry = ldap_first_entry(Lc->ld, SearchResult);

				while(Entry)
				{
					auto_free<PWSTR, free_ldap_value> SubclassValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"subClassOf"));
					auto_free<PWSTR, free_ldap_value> SysAuxilaryclassValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"systemAuxiliaryClass"));
					auto_free<PWSTR, free_ldap_value> AuxilaryclassValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"auxiliaryClass"));

					if(SubclassValues)
						for(PWSTR *Subclass = SubclassValues; *Subclass; Subclass++)
						{
							auto_refct<ClassAttributeEnum> Cae = new ClassAttributeEnum(Lc, *Subclass);

							Dest.ChildProc(*Subclass, Cae);
						}

					if(SysAuxilaryclassValues)
						for(PWSTR *Auxclass = SysAuxilaryclassValues; *Auxclass; Auxclass++)
						{
							auto_refct<ClassAttributeEnum> Cae = new ClassAttributeEnum(Lc, *Auxclass);

							Dest.ChildProc(*Auxclass, Cae);
						}

					if(AuxilaryclassValues)
						for(PWSTR *Auxclass = AuxilaryclassValues; *Auxclass; Auxclass++)
						{
							auto_refct<ClassAttributeEnum> Cae = new ClassAttributeEnum(Lc, *Auxclass);

							Dest.ChildProc(*Auxclass, Cae);
						}

					Entry = ldap_next_entry(Lc->ld, Entry);
				}
			}

			void WalkSubAttributes(LDAPMessage *Entry, PWSTR AttribName, EntriesWalkDest<GUID> &Dest)
			{
				//construct a list of attributes to ask about in a query
				auto_buffer QueryBuff;

				auto_free<PWSTR, free_ldap_value> Attribs = auto_take_(ldap_get_values(Lc->ld, Entry, AttribName));

				if(!Attribs)
					return;

				PCWSTR SearchProlog = L"(|";
				PCWSTR SearchEpilog = L")";
				PCWSTR EntryProlog = L"(lDAPDisplayName=";
				PCWSTR EntryEpilog = L")";

				QueryBuff.append(SearchProlog, wcslen(SearchProlog) * sizeof(WCHAR));
				for(PWSTR *Attrib = Attribs; *Attrib; Attrib++)
				{
					QueryBuff.append(EntryProlog, wcslen(EntryProlog) * sizeof(WCHAR));
					QueryBuff.append(*Attrib, wcslen(*Attrib) * sizeof(WCHAR));
					QueryBuff.append(EntryEpilog, wcslen(EntryEpilog) * sizeof(WCHAR));
				}
				QueryBuff.append(SearchEpilog, wcslen(SearchEpilog) * sizeof(WCHAR));
				QueryBuff.append("\0", sizeof(WCHAR));

				PWSTR Attrs[] = {L"cn", L"schemaIDGUID", NULL};

				ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Schema,CN=Configuration", SbtForest, QueryBuff.get_as<WCHAR>());

				auto_free<LDAPMessage, &free_ldap_message> SearchResult;

				AwaitSearchResult(Lc, OpNum, SearchResult);

				LDAPMessage *SubEntry = ldap_first_entry(Lc->ld, SearchResult);

				while(SubEntry)
				{
					auto_free<PWSTR, free_ldap_value> CnValues = auto_take_(ldap_get_values(Lc->ld, SubEntry, L"cn"));
					auto_free<berval *, free_ldap_value_len> GuidValues = auto_take_(ldap_get_values_len(Lc->ld, SubEntry, L"schemaIDGUID"));

					GUID &Guid = *c_cast<GUID *>(GuidValues[0]->bv_val);

					Dest.EntryProc(CnValues[0], Guid);

					SubEntry = ldap_next_entry(Lc->ld, SubEntry);
				}
			}

			virtual void WalkEntries(EntriesWalkDest<GUID> &Dest) override
			{
				PWSTR Attrs[] = {L"mayContain", L"mustContain", L"systemMayContain", L"systemMustContain", NULL};

				ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Schema,CN=Configuration", SbtForest, L"(&(objectClass=classSchema)(lDAPDisplayName=%s))", ClassCn);

				auto_free<LDAPMessage, &free_ldap_message> SearchResult;

				AwaitSearchResult(Lc, OpNum, SearchResult);

				LDAPMessage *Entry = ldap_first_entry(Lc->ld, SearchResult);

				while(Entry)
				{
					WalkSubAttributes(Entry, L"systemMustContain", Dest);
					WalkSubAttributes(Entry, L"mustContain", Dest);
					Dest.BreakProc();
					WalkSubAttributes(Entry, L"systemMayContain", Dest);
					WalkSubAttributes(Entry, L"mayContain", Dest);

					Entry = ldap_next_entry(Lc->ld, Entry);
				}
			}
		};

		auto_refct<LdapConnection> Lc;
		BOOL HiddenAttrs;

		ClassEnumForAttributes(LdapConnection *Lc, BOOLEAN HiddenAttrs) :
			Lc(Lc),
			HiddenAttrs(HiddenAttrs)
		{}

		virtual void WalkChildren(ChildrenWalkDest<GUID> &Dest) override
		{
			PWSTR Attrs[] = {L"cn", NULL};

			ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Schema,CN=Configuration", SbtForest, L"(&(objectClass=classSchema)(!(objectClassCategory=2))(!(objectClassCategory=3))(defaultHidingValue=%s))", HiddenAttrs ? L"TRUE" : L"FALSE");

			auto_free<LDAPMessage, &free_ldap_message> SearchResult;

			AwaitSearchResult(Lc, OpNum, SearchResult);

			LDAPMessage *Entry = ldap_first_entry(Lc->ld, SearchResult);

			while(Entry)
			{
				auto_free<PWSTR, free_ldap_value> CnValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"cn"));

				auto_refct<ClassAttributeEnum> Cae = new ClassAttributeEnum(Lc, CnValues[0]);

				Dest.ChildProc(CnValues[0], Cae);

				Entry = ldap_next_entry(Lc->ld, Entry);
			}
		}

		virtual void WalkEntries(EntriesWalkDest<GUID> &Dest) override
		{} //no direct entries
	};

	struct ExtendedRightsEnum : public Enumerator<GUID>
	{
		auto_refct<LdapConnection> Lc;
		ULONG RequiredValidAccess;

		ExtendedRightsEnum(LdapConnection *Lc, ULONG RequiredValidAccess) :
			Lc(Lc),
			RequiredValidAccess(RequiredValidAccess)
		{}

		virtual void WalkChildren(ChildrenWalkDest<GUID> &Dest) override
		{} //no children

		virtual void WalkEntries(EntriesWalkDest<GUID> &Dest) override
		{
			PWSTR Attrs[] = {L"cn", L"rightsGUID", NULL};

			ULONG OpNum = StartLdapSearch(Lc, LDAP_SCOPE_ONELEVEL, Attrs, L"CN=Extended-Rights,CN=Configuration", SbtForest, L"(&(validAccesses:1.2.840.113556.1.4.803:=%u)(objectClass=controlAccessRight))", RequiredValidAccess); //1.2.840.113556.1.4.803 is LDAP_MATCHING_RULE_BIT_AND

			auto_free<LDAPMessage, &free_ldap_message> SearchResult;

			AwaitSearchResult(Lc, OpNum, SearchResult);

			LDAPMessage *Entry = ldap_first_entry(Lc->ld, SearchResult);

			while(Entry)
			{
				auto_free<PWSTR, free_ldap_value> CnValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"cn"));
				auto_free<PWSTR, free_ldap_value> RightsValues = auto_take_(ldap_get_values(Lc->ld, Entry, L"rightsGUID"));

				auto_szpod<GUID> RightsGuid = TextToGuid(RightsValues[0]);

				if(!(CnValues[0] && RightsGuid))
					RaiseTextError(L"Malformed extended-right", NULL, CnValues ? CnValues[0] : NULL);

				Dest.EntryProc(CnValues[0], *RightsGuid);

				Entry = ldap_next_entry(Lc->ld, Entry);
			}
		}
	};

public:
	virtual void WalkChildren(ChildrenWalkDest<GUID> &Dest) override
	{
		auto_refct<Enumerator<GUID> > Property = new ClassEnumForAttributes(Lc, FALSE);
		auto_refct<Enumerator<GUID> > ExtraProperty = new ClassEnumForAttributes(Lc, TRUE);
		auto_refct<Enumerator<GUID> > PropertySet = new ExtendedRightsEnum(Lc, 0x0030);
		auto_refct<Enumerator<GUID> > ExtendedRight = new ExtendedRightsEnum(Lc, 0x0100);
		auto_refct<Enumerator<GUID> > ValidatedRight = new ExtendedRightsEnum(Lc, 0x0008);
		auto_refct<Enumerator<GUID> > Class = new ObjInhTypeGuidEnumerator(Lc);

		Dest.ChildProc(L"Properties", Property);
		Dest.ChildProc(L"Extra Properties", ExtraProperty);
		Dest.ChildProc(L"Property Sets", PropertySet);
		Dest.ChildProc(L"Extended", ExtendedRight);
		Dest.ChildProc(L"Validated Write", ValidatedRight);
		Dest.ChildProc(L"Classes", Class);
	}

	virtual void WalkEntries(EntriesWalkDest<GUID> &Dest) override
	{} //no direct entries
};

}//namespace Ldap